/*
 * Copyright 2014-2022 Arx Libertatis Team (see the AUTHORS file)
 *
 * This file is part of Arx Libertatis.
 *
 * Original source is copyright 2002 Robert Ramey
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#ifndef ARX_UTIL_HANDLETYPE_H
#define ARX_UTIL_HANDLETYPE_H

#include <limits>
#include <ostream>
#include <type_traits>

#include "platform/Platform.h"
#include "util/Cast.h"


namespace util {

template <typename TAG, typename T, T Invalid = T(-1)>
class HandleType {
	
	T t = Invalid;
	
public:
	
	template <typename O, typename = std::enable_if_t<std::is_integral_v<O>>>
	explicit constexpr HandleType(const O value) noexcept : t(to<T>(value)) { }
	constexpr HandleType() noexcept = default;
	
	[[nodiscard]] constexpr bool operator==(const HandleType & rhs) const noexcept { return t == rhs.t; }
	[[nodiscard]] constexpr bool operator!=(const HandleType & rhs) const noexcept { return t != rhs.t; }
	
	[[nodiscard]] constexpr T handleData() const noexcept { return t; }
	
	template <typename O, typename = std::enable_if_t<std::is_integral_v<O>>>
	[[nodiscard]] explicit constexpr operator O() const noexcept {
		arx_assume(t != Invalid);
		return to<O>(t);
	}
	
	[[nodiscard]] explicit constexpr operator bool() const noexcept { return t != Invalid; }
	[[nodiscard]] constexpr bool operator!() const noexcept { return t == Invalid; }
	
	[[nodiscard]] static constexpr T max() { return std::numeric_limits<T>::max(); }
	
};

template <typename TAG, typename T, T Invalid>
std::ostream & operator<<(std::ostream & s, HandleType<TAG, T, Invalid> v) {
	if(v.handleData() != Invalid) {
		s << v.handleData();
	} else {
		s << "inv";
	}
	return s;
}

} // namespace util

#endif // ARX_UTIL_HANDLETYPE_H
